import networkx as nx
import numpy as np
import os
import json
import pickle

def load_all_graphs(path):
    """
    读取所有的网络
    """
    with open(path, 'rb') as file:
        all_graphs = pickle.load(file)
    
    return all_graphs


def bulid_network(event_list, member_list):
    """
    以event为单位构建社交网络,图(networkX)的结构如下
        -id: 活动id (g.id)
        -group_id: 所属社团id (g.group_id)
        -time: 活动开始时间 (g.time)
        
        -node:
            --member: 该节点所表示的成员id (g.nodes)
            --response: 该节点所表示的成员的resoponse (g.nodes[id]["response"])
            --feature(numpy类型): 该节点所表示的成员的featrue (g.nodes[id]["feature"])
        
        -edge: 两个节点的边,两成员有共同topic视为有边,权重为featrue的cos_similarity;
               若无共同topic则无边相连 (g[id_1][id_2]["weight"])
    """
    graphs = []
    count = 1
    for event_item in event_list:
        print('Event_', count,'is doing!')
        count += 1
        feature_dim, event_topics_dict = count_topics(event_item, member_list)
        if(feature_dim==0):
            continue

        # init graph 
        graph = nx.Graph()
        graph.id = event_item["id"]
        graph.group_id = event_item["group_id"]
        graph.time = event_item["start_time"]

        # form node feature and add it
        for member, response in event_item["member and response"].items():
            for index in range(len(member_list)):
                if(member_list[index]["id"] == str(member)):
                    member_topics = member_list[index]["topics"]
                    break
            cur_feature = form_feature(member_topics, feature_dim, event_topics_dict)
            graph.add_node(member, response=response, feature=cur_feature)

        # count weight of edge and add it
        for member_i, _ in event_item["member and response"].items():
            for member_j, _ in event_item["member and response"].items():
                if(member_i==member_j or graph.has_edge(member_i, member_j)):
                    continue
                
                feature_i = graph.nodes.data()[member_i]['feature']
                feature_j = graph.nodes.data()[member_j]['feature']
                weight = weight_edge(feature_i, feature_j)
                if(weight!=0):
                    graph.add_edge(member_i, member_j, weight=weight)
        
        graphs.append(graph)
        
    return graphs


def count_topics(event, member_list):
    """
    计数event的所有成员topic的数量,该数量即为node feature的维度
    """
    event_set = set()
    for member, _ in event["member and response"].items():
        temp_topics = []
        for index in range(len(member_list)):
            if(member_list[index]["id"] == str(member)):
                temp_topics = member_list[index]["topics"]
                break
        for topic in temp_topics:
            event_set.add(topic)
    all_topics = list(event_set)
    feature_dim = len(all_topics)

    return feature_dim, all_topics


def form_feature(member_topics, feature_dim, event_topics_dict):
    """
    以topics内容,形成one-hot方式的feature;
    考虑到海王的感情会分散,对feature重新评估(平均值标准化)
    """
    feature = np.zeros(feature_dim, dtype=float)
    for index, topic in enumerate(event_topics_dict):
        if(member_topics.__contains__(topic)):
            feature[index] = 1
    
    if(feature.sum()!=0):
        feature = feature / feature.sum() * 10
    
    return feature


def weight_edge(feature_i, feature_j):
    """
    量化edge权重:使用cos_similarity策略,返回(0,1]
    """
    feature_i = np.mat(feature_i)
    feature_j = np.mat(feature_j)
    num = float(feature_i * feature_j.T)
    if(num==0):
        return 0

    denom = np.linalg.norm(feature_i) * np.linalg.norm(feature_j)
    cos = num / denom
    sim = 0.5 + 0.5 * cos
    
    return sim

# Modified 2022/4/16
def read_data(path, rebuild=False):
    event_path = os.path.join(path, 'event_list.json')
    if not os.path.exists(event_path):
        err_msg = "Cannot find {}.json. Path is {}".format("event_list", event_path)
        raise BaseException(err_msg)
    if os.path.exists(event_path) and rebuild==False:
        with open (event_path) as f:
            event_list = json.load(f)       
        print("load event info complete")
    
    member_path = os.path.join(path, 'member_list.json')     
    if not os.path.exists(member_path):
        err_msg = "Cannot find {}.json. Path is {}".format("member_list", member_path)
        raise BaseException(err_msg)
    if os.path.exists(member_path) and rebuild==False:
        with open(member_path) as f:
            member_list = json.load(f)
        print("load member info complete")

    group_path = os.path.join(path, 'group_list.json')     
    if not os.path.exists(group_path):
        err_msg = "Cannot find {}.json. Path is {}".format("group_list", group_path)
        raise BaseException(err_msg)
    if os.path.exists(group_path) and rebuild==False:
        with open(group_path) as f:
            group_list = json.load(f)
        print("load group info complete")

    topic_json_path = os.path.join(path, "topic_dict.json")
    if not os.path.exists(topic_json_path):
        err_msg = "Cannot find {}.json. Path is {}".format("topic_dict", topic_json_path)
        raise BaseException(err_msg)
    topic_dict = dict()
    if os.path.exists(topic_json_path) and (not rebuild):
        with open(topic_json_path) as f:
            topic_dict = json.load(f)
        print("load topic info complete")
    
    return event_list, topic_dict, member_list, group_list